package com.eee.machine.utils.idGen;

import java.util.Date;

/**
 * 生成分布式id工具类
 */
public class DistributedIdWorker {

    private static final long workerId;

    //private static final  long BASE_TIMESTAMP = 1425852741828L;
    private static final long BASE_TIMESTAMP = 1361753741828L;

    //private static final  long BASE_TIMESTAMP = 1424055579478L;

    private long sequence = 0L;

    private static final long WORKID_BITS = 8L;

    private static final long MAX_WORERKID = -1L ^ -1L << WORKID_BITS;

    private static final long SEQUENCE_BITS = 6L;

    private static final long WORKERID_SHIFT = SEQUENCE_BITS;

    private static final long TIMESTAMP_LEFTSHIFT = SEQUENCE_BITS + WORKID_BITS;

    private static final long MAX_SEQUENCE = -1L ^ -1L << SEQUENCE_BITS;

    private long lastTimestamp = -1L;

    private static DistributedIdWorker worker = new DistributedIdWorker();

    static {
        String id = DistributeConfiguration.getInstance().getValue("workerId");
        workerId = Long.parseLong(id);
        if (workerId > MAX_WORERKID || workerId < 0) {
            throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", MAX_WORERKID));
        }
    }

    private DistributedIdWorker() {

    }

    public static long createId() {
        return worker.nextId();
    }

    private synchronized long nextId() {
        long timestamp = this.timeGen();
        if (this.lastTimestamp == timestamp) {
            this.sequence = (this.sequence + 1) & MAX_SEQUENCE;
            if (this.sequence == 0) {
                timestamp = this.tilNextMillis(this.lastTimestamp);
            }
        } else {
            this.sequence = 0;
        }
        if (timestamp < this.lastTimestamp) {
            throw new RuntimeException(
                    String.format(
                            "Clock moved backwards.  Refusing to generate idGen for %d milliseconds",
                            this.lastTimestamp - timestamp));
        }
        this.lastTimestamp = timestamp;
        return (timestamp - BASE_TIMESTAMP << TIMESTAMP_LEFTSHIFT) | (workerId << WORKERID_SHIFT) | (this.sequence);
    }

    private long tilNextMillis(final long lastTimestamp) {
        long timestamp = this.timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = this.timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }

    public static void main(String[] args) {
        System.out.println(createId());
        System.out.println(String.valueOf(createId()).length());
        long id = 18014398509481983l;
        String s = Long.toBinaryString(id);
        System.out.println(s);
        System.out.println(s.length());
        s = s.substring(0, s.length() - (int) TIMESTAMP_LEFTSHIFT);
        System.out.println(s.length());
        id = Long.parseLong(s, 2);
        System.out.println("idGen==" + id);
        Date d = new Date(id + BASE_TIMESTAMP);
        System.out.println(d.toLocaleString());
        d = new Date(BASE_TIMESTAMP);
        System.out.println(d.toLocaleString());

        id = 10000000000l;
        s = Long.toBinaryString(id);
        System.out.println(s.length());
        s = s.substring(0, s.length() - (int) TIMESTAMP_LEFTSHIFT);
        System.out.println(s.length());
        id = Long.parseLong(s, 2);
        System.out.println(id);
        System.out.println(System.currentTimeMillis() - id);

        System.out.println(System.currentTimeMillis());
        s = "111111111111111111111111111111111111111";
        System.out.println(s.length());
        id = Long.parseLong(s, 2);
        System.out.println(id / 1000 / 60 / 60 / 24 / 365);

        System.out.println(System.currentTimeMillis() / 1000 / 60 / 60 / 24 / 365);
    }
}
